home *** CD-ROM | disk | FTP | other *** search
/ Monster Media 1996 #15 / Monster Media Number 15 (Monster Media)(July 1996).ISO / prog_c / cppnl011.zip / CPPNL011.TXT next >
Text File  |  1996-05-30  |  13KB  |  430 lines

  1. Issue #011
  2. May, 1996
  3.  
  4.  
  5. Contents:
  6.  
  7. The Meaning of "Static"
  8. Local Statics and Constructors/Destructors
  9. Introduction to Stream I/O Part 6 - Seeking in Files
  10. Using C++ as a Better C Part 11 - Jumping Past Initialization
  11. Introduction to Templates Part 3 - Template Arguments
  12.  
  13.  
  14. THE MEANING OF "STATIC"
  15.  
  16. Someone asked about the meaning of the term "static" in C++.  This
  17. particular term is perhaps the most overworked one in the language.
  18. It's both a descriptive word and a C++ keyword that is used in various
  19. ways.
  20.  
  21. "static" as a descriptive term refers to the lifetime of C++ memory or
  22. storage locations.  There are several types of storage:
  23.  
  24.         - static
  25.  
  26.         - dynamic (heap)
  27.  
  28.         - auto (stack)
  29.  
  30. A typical storage layout scheme will have the following arrangement,
  31. from lowest to highest virtual memory address:
  32.  
  33.         text (program code)
  34.  
  35.         static (initialized and uninitialized data)
  36.  
  37.         heap
  38.  
  39.         (large virtual address space gap)
  40.  
  41.         stack
  42.  
  43. with the heap and stack growing toward each other.  The C++ draft
  44. standard does not mandate this arrangement, and this example is only an
  45. illustration of one way of doing it.
  46.  
  47. Static storage thus refers to memory locations that persist for the
  48. life of the program; global variables are static.  Stack storage comes
  49. and goes as functions are called ("stack frames"), and heap storage is
  50. allocated and deallocated using operators new and delete.  Note that
  51. usage like:
  52.  
  53.         void f()
  54.         {
  55.                 static int x = 37;
  56.         }
  57.  
  58. also refers to storage that persists throughout the program, even
  59. though x cannot be used outside of f() to refer to that storage.
  60.  
  61. So we might say that "static" as a descriptive term is used to
  62. describe the lifetime of memory locations.  static can also be used to
  63. describe the visibility of objects.  For example:
  64.  
  65.         static int x = 37;
  66.  
  67.         static void f() {}
  68.  
  69. says that x and f() are not visible outside the source file where
  70. they're defined, and
  71.  
  72.         void f()
  73.         {
  74.                 static int x = 47;
  75.         }
  76.  
  77. says that x is not visible outside of f().  Visibility and lifetime
  78. are not the same thing;  an object can exist without being visible.
  79.  
  80. So far we've covered the uses of static that are found in C.  C++ adds
  81. a couple of additional twists.  It is possible to have static members
  82. of a C++ class:
  83.  
  84.         class A {
  85.         public:
  86.                 static int x;
  87.                 static void f();
  88.                 int y;
  89.         };
  90.  
  91.         int A::x = 0;
  92.  
  93.         void A::f() {}
  94.  
  95. A static data member like A::x is shared across all object instances
  96. of A.  That is, if I define two object instances:
  97.  
  98.         A a1;
  99.         A a2;
  100.  
  101. then they have the same x but y is different between them.  A static
  102. data member is useful to share information between object instances.
  103. For example, in issue #010 we talked about using a specialized
  104. allocator on a per-class basis to allocate memory for object
  105. instances, and a static member "freelist" was used as part of the
  106. implementation of this scheme.
  107.  
  108. A static function member, such as A::f(), can be used to provide
  109. utility functions to a class.  For example, with a class representing
  110. calendar dates, a function that tells whether a given year is a leap
  111. year might best be represented as a static function.  The function is
  112. related to the operation of the class but doesn't operate on
  113. particular object instances (actual calendar dates) of the class.
  114. Such a function could be made global, but it's cleaner to have the
  115. function as part of the Date package:
  116.  
  117.         class Date {
  118.                 static int is_leap(int year);   // use bool if available
  119.         public:
  120.                 // stuff
  121.         };
  122.  
  123. In this example, is_leap() is private to Date and can only be used
  124. within member functions of Date, instead of by the whole program.
  125.  
  126. static meaning "local to a file" has been devalued somewhat by the
  127. introduction of C++ namespaces; the draft standard states that use of
  128. static is deprecated for objects in namespace scope.  For example,
  129. saying:
  130.  
  131.         static int x;
  132.         static void f() {}
  133.  
  134. is equivalent to:
  135.  
  136.         namespace {
  137.                 int x;
  138.                 void f() {}
  139.         }
  140.  
  141. That is, an unnamed namespace is used to wrap the static
  142. declarations.  All unnamed namespaces in a single source file
  143. (translation unit) are part of the same namespace and differ from
  144. similar namespaces in other translation units.
  145.  
  146.  
  147. LOCAL STATICS AND CONSTRUCTORS/DESTRUCTORS
  148.  
  149. There's one additional interesting angle on the use of static.
  150. Suppose that you have:
  151.  
  152.         class A {
  153.         public:
  154.                 A();
  155.                 ~A();
  156.         };
  157.  
  158.         void f()
  159.         {
  160.                 static A a;
  161.         }
  162.  
  163. This object has a constructor that must be called at some point.  But
  164. we can't call the constructor each time that f() is called, because
  165. the object is static, that is, exists for the life of the program, and
  166. should be constructed only once.  The draft standard says that such an
  167. object should be constructed once, the first time execution passes
  168. through its declaration.
  169.  
  170. This might be implemented internally by a compiler as:
  171.  
  172.         void f()
  173.         {
  174.                 static int __first = 1;
  175.                 static A a;
  176.  
  177.                 if (__first) {
  178.                         a.A::A();       // conceptual, not legal syntax
  179.                         __first = 0;
  180.                 }
  181.  
  182.                 // other processing
  183.         }
  184.  
  185. If f() is never called, then the object will not be constructed.  If
  186. it is constructed, it must be destructed when the program terminates.
  187.  
  188.  
  189. INTRODUCTION TO STREAM I/O PART 6 - SEEKING IN FILES
  190.  
  191. In earlier issues we talked about streambufs, the underlying buffer
  192. used in I/O operations.  One of the things that you can do with a
  193. buffer is position its pointer at various places.  For example:
  194.  
  195.         #include <fstream.h>
  196.  
  197.         int main()
  198.         {
  199.                 ofstream ofs("xxx");
  200.                 if (!ofs)
  201.                         ; // give error
  202.  
  203.                 ofs << ' ';
  204.  
  205.                 ofs << "abc";
  206.  
  207.                 streampos pos = ofs.tellp();
  208.                 ofs.seekp(0);
  209.                 ofs << 'x';
  210.                 ofs.seekp(pos);
  211.  
  212.                 ofs << "def\n";
  213.  
  214.                 return 0;
  215.         }
  216.  
  217. Here we have an output file stream attached to a file "xxx".  We open
  218. this file and write a single blank character at the beginning of it.
  219. In this particular application this character is a status character of
  220. some sort that we will update from time to time.
  221.  
  222. After writing the status character, we write some characters to the
  223. file, at which point we wish to update the status character.  To do
  224. this, we save the current position of the file using tellp(), seek to
  225. the beginning, write the character, and then seek back to where we
  226. were, at which point we can write some more characters.
  227.  
  228. Note that "streampos" is a defined type of some kind rather than
  229. simply a fixed fundamental type like "long".  You should not assume
  230. particular types when working with file offsets and positions, but
  231. instead save the value returned by tellp() and then use it later.
  232.  
  233. In a similar way, it's tricky to use absolute file offsets other than
  234. 0 when seeking in files.  For example, there are issues with binary
  235. files and with CR/LF translation.  You may be assuming that a newline
  236. takes two characters when it only takes one, or vice versa.
  237.  
  238. seekp() also has a two-parameter version:
  239.  
  240.         ofs.seekp(pos, ios::beg);  // from beginning
  241.  
  242.         ofs.seekp(pos, ios::cur);  // from current position
  243.  
  244.         ofs.seekp(pos, ios::end);  // from end
  245.  
  246. As we've said before, this area is in a state of flux, pending
  247. standardization.  So future usage may be somewhat different than shown
  248. here.
  249.  
  250.  
  251. USING C++ AS A BETTER C PART 11 - JUMPING PAST INITIALIZATION
  252.  
  253. As we've seen in several examples in previous newsletters, C++ does
  254. much more with initializing objects than C does.  For example, class
  255. objects have constructors, and global objects can have general
  256. initializers that cannot be evaluated at compile time.
  257.  
  258. Another difference between C and C++ is the restriction C++ places on
  259. transferring control past an initialization.  For example, the
  260. following is valid C but invalid C++:
  261.  
  262.         #include <stdio.h>
  263.  
  264.         int main()
  265.         {
  266.                 goto xxx;
  267.  
  268.                 {
  269.                         int x = 0;
  270.         xxx:
  271.                         printf("%d\n", x);
  272.                 }
  273.  
  274.                 return 0;
  275.         }
  276.  
  277. With one compiler, compiling and executing this program as C code
  278. results in a value of 512 being printed, that is, garbage is output.
  279. Thus the restriction makes sense.
  280.  
  281. The use of goto statements is best avoided except in carefully
  282. structured situations such as jumping to the end of a block.  Jumping
  283. over initializations can also occur with switch/case statements.
  284.  
  285.  
  286. INTRODUCTION TO TEMPLATES PART 3 - TEMPLATE ARGUMENTS
  287.  
  288. In previous issues we talked about setting up a class template:
  289.  
  290.         template <class T> class A {
  291.                 T x;
  292.                 // stuff
  293.         };
  294.  
  295. When this template is used:
  296.  
  297.         A<double> a;
  298.  
  299. the type "double" gets bound to the formal type parameter T, part of a
  300. process known as instantiation.  In this example, the instantiated
  301. class will have a data member "x" of type double.
  302.  
  303. What sorts of arguments can be used with templates?  Type arguments
  304. are allowed, including "void":
  305.  
  306.         template <class T> class A {
  307.                 T* x;
  308.         };
  309.  
  310.         A<void> a;
  311.  
  312. The member x will be of type void* in this case.  In the earlier
  313. example, using void as a type argument would result in an
  314. instantiation error, because a data member of a class (or any object
  315. for that matter) cannot be of type void.
  316.  
  317. Usage like:
  318.  
  319.         A<int [37][47]> a1;
  320.  
  321.         A< A<void> > a2;
  322.  
  323. is also valid.  Note that in the second example, the second space is
  324. required, because ">>" is an operator meaning right shift.
  325.  
  326. It's also possible to have non-type arguments.  Constant expressions
  327. can be used:
  328.  
  329.         template <class T, int n> class A {
  330.                 T vec[n];
  331.         };
  332.  
  333.         A<float, 100> a;
  334.  
  335. This is useful in specifying the size of an internal data structure,
  336. in this example a vector of float[100].  The size could also be
  337. specified via a constructor, but in that case the size would not be
  338. known at compile time and therefore dynamic storage would have to be
  339. used to allocate the vector.
  340.  
  341. The address of an external object can be used:
  342.  
  343.         template <char* cp> struct A { /* ... */ };
  344.  
  345.         char c;
  346.  
  347.         A<&c> a;
  348.  
  349. or you can use the address of a function:
  350.  
  351.         template <void (*fp)(int)> struct A { /* ... */ };
  352.  
  353.         void f(int) {}
  354.  
  355.         A<f> a;
  356.  
  357. This latter case might be useful if you want to pass in a pointer of a
  358. function to be used internally within the template, for example, to
  359. compare elements of a vector.
  360.  
  361. Some other kinds of constructs are not permitted as arguments:
  362.  
  363.         - a constant expression of floating type
  364.  
  365.         - addresses of array elements
  366.  
  367.         - addresses of non-static class members
  368.  
  369.         - local types
  370.  
  371.         - addresses of local objects
  372.  
  373. Two template classes (a template class is an instantiated class
  374. template, that is, a template with specific arguments) refer to the
  375. same class if their template names are identical and in the same scope
  376. and their arguments have identical values.  For example:
  377.  
  378.         template <class T, int n> struct A {};
  379.  
  380.         typedef short* TT;
  381.  
  382.         A<short*, 100> a1;
  383.         A<TT, 25*4> a2;
  384.  
  385. a1 and a2 are of the same type.
  386.  
  387.  
  388. ACKNOWLEDGEMENTS
  389.  
  390. Thanks to Nathan Myers, Eric Nagler, David Nelson, Terry Rudd,
  391. Jonathan Schilling, and Clay Wilson for help with proofreading.
  392.  
  393.  
  394. SUBSCRIPTION INFORMATION / BACK ISSUES
  395.  
  396. To subscribe to the newsletter, send mail to majordomo@world.std.com
  397. with this line as its message body:
  398.  
  399. subscribe c_plus_plus
  400.  
  401. Back issues are available via FTP from:
  402.  
  403.         rmii.com /pub2/glenm/newslett
  404.  
  405. or on the Web at:
  406.  
  407.         http://www.rmii.com/~glenm
  408.  
  409. There is also a Java newsletter.  To subscribe to it, say:
  410.  
  411. subscribe java_letter
  412.  
  413. using the same majordomo@world.std.com address.
  414.  
  415. -------------------------
  416.  
  417. Copyright (c) 1996 Glen McCluskey.  All Rights Reserved.
  418.  
  419. This newsletter may be further distributed provided that it is copied
  420. in its entirety, including the newsletter number at the top and the
  421. copyright and contact information at the bottom.
  422.  
  423. Glen McCluskey & Associates
  424. Professional C++ Consulting
  425. Internet: glenm@glenmccl.com
  426. Phone: (800) 722-1613 or (970) 490-2462
  427. Fax: (970) 490-2463
  428. FTP: rmii.com /pub2/glenm/newslett (for back issues)
  429. Web: http://www.rmii.com/~glenm
  430.